package display;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;

import main.Agent;
import main.Grid;
import main.Main;
import main.Agent.AgentName;


@SuppressWarnings("serial")
/**
 * Game Panel where all the action takes place 
 *
 */
public class MainGamePanel extends JPanel {
	
	private static MainGamePanel instance;
	private static int gameNum = 0;
	
	//-- Grid
	private static final int GRID_SIZE = 8;
	
	private Grid currentGrid;
	
	//-- GUI Elements
	private JPanel gridPanel, labelPanel;
	private JButton [][] jButtons = new JButton[8][8];
	private JLabel WhiteLabel, BlackLabel;
	
	private JButton resetButton;
	public JButton pauseButton;
	
	private static ImageIcon white, black, empty;
	
	//-- Players
	public enum AgentColor {BLACK, WHITE};
	
	private Agent blackAgent;
	private Agent whiteAgent;
	
	private AgentName[] agents = {AgentName.HUMAN, AgentName.MINMAX, AgentName.WEIGHTED_MINMAX, AgentName.PRIORITY, AgentName.GREEDY, AgentName.WEIGHTED};
	
	private boolean blackPlayer = false; //It will be set after starting the game
	private boolean whitePlayer = false;
	private boolean winner = false;
	
	private int whiteScore, blackScore;
	
	//-- Timer
	Timer timer;
	long delay = 1000, period = 300;
	public boolean pause = false;

	/**
	 * Static access method for singleton panel
	 * @return Current instance of MainGamePanel 
	 */
	public static MainGamePanel GetInstance()
	{
		if(instance == null)
			instance = new MainGamePanel();
		return instance;
	}
	
	/**
	 * Holds all the game elements
	 */
	private MainGamePanel(){
		if(white == null || black == null){
			white = new ImageIcon("Res/white.png");
			black = new ImageIcon("Res/black.png");
			empty = new ImageIcon("1x1.png");
		}
		
		blackAgent = new Agent(AgentName.HUMAN, AgentColor.BLACK); //By default Human vs Human
		whiteAgent = new Agent(AgentName.HUMAN, AgentColor.WHITE);
		
		WhiteLabel = new JLabel("White: ");
		BlackLabel = new JLabel("Black: ");
		WhiteLabel.setOpaque(true);
		BlackLabel.setOpaque(true);
				
		this.setLayout(new BorderLayout());
		initialize();
	}

	/**
	 * Adds the contents to the main game panel
	 */
	private void initialize() {
				
		//Add the agents panel to the bottom of the MainGamePanel
		this.add(AgentsPanel(), BorderLayout.SOUTH);
		
		currentGrid = new Grid();
		gridPanel = GameBoard();
		this.add(gridPanel, BorderLayout.CENTER);
		
		labelPanel = LabelPanel();
		this.add(labelPanel,BorderLayout.NORTH);
		
		this.validate();
	}
	
	/**
	 * This method updates the score and checks if it's the next player's turn as
	 * well as checks if anyone has won yet.
	 * @return The timer task used in the timer
	 */
	private TimerTask getTimerTask(){

		TimerTask timerTask = new TimerTask() {
			
			@Override
			public void run() {
				if(!pause){
					updateScore();
					if((blackPlayer && blackAgent.getName() != AgentName.HUMAN) 
							|| (whitePlayer && whiteAgent.getName() != AgentName.HUMAN)){
						setTurn();
					}
					checkVictory();
				}
			}

		};
		return timerTask;
	}
	
	/**
	 * Checks if victory conditions have been met and then acts accordingly
	 */
	private void checkVictory() {
		if(winner) return;
		if(blackScore + whiteScore == 64)
		{
			winner = true;
			updateScore();
			stopTimers();
			return;
		}
		if(currentGrid.GetLegalMoves('B').size() == 0 && currentGrid.GetLegalMoves('W').size() == 0){
			winner = true;
			updateScore();
			stopTimers();
		}
	}
	
	/**
	 * Pauses or unpauses the timers running in the game.
	 * @param onoff pause = true, unpause = false
	 */
	public synchronized void pauseTimers(boolean onoff){
		pause = onoff;
	}
	
	/**
	 * Permanently stops the timers in the current game.
	 * Start game will need to be called in order to continue.
	 */
	public synchronized void stopTimers(){
		if(timer != null){
			timer.cancel();
			timer = null;
		}
	}
	
	/**
	 * Counts the number of pieces on the board and updates the player scores
	 */
	public synchronized void updateScore(){
		whiteScore = 0;
		blackScore = 0;
		
		for(int i = 0; i < GRID_SIZE; i++){
			for(int j = 0; j < GRID_SIZE; j++){
				if(jButtons[i][j].getIcon() == white) whiteScore++;
				else if(jButtons[i][j].getIcon() == black) blackScore++;
			}
		}
		
		setLabels();
	}
	
	/**
	 * Updates the text labels at the top to reflect the score and winner status.
	 */
	public void setLabels(){
		WhiteLabel.setText("White: " + whiteScore);
		BlackLabel.setText("Black: " + blackScore);
		if(winner){
			WhiteLabel.setBackground(Color.WHITE);
			BlackLabel.setBackground(Color.WHITE);
			if(blackScore > whiteScore){
				BlackLabel.setBackground(Color.YELLOW);
				BlackLabel.setText(BlackLabel.getText() + " !!!WINNER!!!");
				Main.logger.logWinner(blackAgent);
			}else if(whiteScore > blackScore){
				WhiteLabel.setBackground(Color.YELLOW);
				WhiteLabel.setText(WhiteLabel.getText() + " !!!WINNER!!!");
				Main.logger.logWinner(whiteAgent);
			}else{
				BlackLabel.setBackground(Color.YELLOW);
				WhiteLabel.setBackground(Color.YELLOW);
				BlackLabel.setText(BlackLabel.getText() + " !!!TIE!!!");
				WhiteLabel.setText(WhiteLabel.getText() + " !!!TIE!!!");
				Main.logger.logWinner(blackAgent);
				Main.logger.logWinner(whiteAgent);
			}
			return;
		}
		if(blackPlayer){
			BlackLabel.setBackground(Color.GREEN);
			WhiteLabel.setBackground(Color.WHITE);
		}else{
			BlackLabel.setBackground(Color.WHITE);
			WhiteLabel.setBackground(Color.GREEN);
		}
	}
	
	/**
	 * Updates the graphical board to reflect the data board
	 */
	public void refreshButtons()
	{
		for(int i = 0; i < GRID_SIZE; i++){
			for(int j = 0; j < GRID_SIZE; j++){
				switch(currentGrid.getElementAt(i, j)){
				case 'W':
					jButtons[i][j].setIcon(white); 
					break;
				case 'B':
					jButtons[i][j].setIcon(black);
					break;
				default:
					jButtons[i][j].setIcon(empty);
					break;
				}
			}
		}
	}
	
	/**
	 * Shows score and victory status
	 * @return A panel containing labels
	 */
	public JPanel LabelPanel(){
		JPanel labelPanel = new JPanel(new GridLayout(1,2));
		updateScore();
		labelPanel.add(WhiteLabel);
		labelPanel.add(BlackLabel);
		return labelPanel;
	}
	
	/**
	 * Shows visually the status of the board
	 * @return A panel containing the grid representation
	 */
	public JPanel GameBoard(){
		JPanel grid = new JPanel(new GridLayout(GRID_SIZE, GRID_SIZE));
		
		for(int i = 0; i < GRID_SIZE; i++){
			for(int j = 0; j < GRID_SIZE; j++){
				switch(currentGrid.getElementAt(i, j)){
				case 'W':
					jButtons[i][j] = new JButton(white);
					break;
				case 'B':
					jButtons[i][j] = new JButton(black);
					break;
				default:
					jButtons[i][j] = new JButton(empty);
					break;
				}
				Color color = new Color(170, 160, 160);
				jButtons[i][j].setBackground(color);
				jButtons[i][j].addActionListener(new ButtonListener(jButtons[i][j], i, j));
				grid.add(jButtons[i][j]);
			}
		}

		return grid;
	}
	
	
	/**
	 * Creates combo box for selecting black and white players
	 * and a button for restarting the game
	 * @return a JPanel containing 2 JLabels, 2 comboboxes and 1 button
	 */
	private JPanel AgentsPanel() {
		//Panel that contains the combobox and reset button
		JPanel agentsPanel = new JPanel();
		
		//Adding black label
		JLabel blackLabel = new JLabel("Black Player: ");
		agentsPanel.add(blackLabel, BorderLayout.NORTH);
		
		//Adding ComboBox to select black player
		JComboBox blackPlayerAgent = new JComboBox(agents);
		blackPlayerAgent.setSelectedItem(blackAgent.getName());
		blackPlayerAgent.addActionListener(new ComboBoxListener(blackPlayerAgent, AgentColor.BLACK));
		agentsPanel.add(blackPlayerAgent, BorderLayout.NORTH);
		
		//Adding white label
		JLabel whiteLabel = new JLabel("White Player: ");
		agentsPanel.add(whiteLabel, BorderLayout.NORTH);
		
		//Adding ComboBox to select white player
		JComboBox whitePlayerAgent = new JComboBox(agents);
		whitePlayerAgent.setSelectedItem(whiteAgent.getName());
		whitePlayerAgent.addActionListener(new ComboBoxListener(whitePlayerAgent, AgentColor.WHITE));
		agentsPanel.add(whitePlayerAgent, BorderLayout.NORTH);
		
		//Restart Game option
		resetButton = new JButton("Start Game");
		resetButton.addActionListener(new ButtonListener(resetButton));
		agentsPanel.add(resetButton, BorderLayout.SOUTH);
		
		//Pause Game option
		pauseButton = new JButton("Pause Game");
		pauseButton.addActionListener(new ButtonListener(pauseButton));
		agentsPanel.add(pauseButton, BorderLayout.SOUTH);
		
		return agentsPanel;
	}
	
	
	/**
	 * Resets the board for a new game.
	 */
	public void reset() {
		if(timer!= null){
			timer.cancel();
		}
		timer = new Timer("GameTimer");
		timer.schedule(getTimerTask(), delay, period);
		
		System.out.println("\n");
		System.out.println("*********************");
		System.out.println("*** New Game Started: *** BlackPlayer as " + blackAgent.getName() + " vs WhitePlayer as " + whiteAgent.getName() + " ***");
		System.out.println("*********************");
		Main.logger.logNewGame(gameNum++);
		Main.logger.logAgent(blackAgent);
		Main.logger.logAgent(whiteAgent);
		winner = false;
		Main.logger.logGrid('S', getCurrentGrid());
		this.removeAll();
		initialize();
		blackPlayer = false;
		setTurn();
		System.out.println(getCurrentGrid().toString());
	}
	
	/**
	 * Notifies the next player that it is there turn to play
	 */
	public void play()
	{
		//Automatic play if the player is not human
		if(whitePlayer && !whiteAgent.getName().equals(AgentName.HUMAN))
		{
			whiteAgent.play();
			if(blackAgent.getName().equals(AgentName.HUMAN))
				setTurn();
		}
		else if(blackPlayer && !blackAgent.getName().equals(AgentName.HUMAN))
		{
			blackAgent.play();
			if(whiteAgent.getName().equals(AgentName.HUMAN))
				setTurn();
		}
	}


	/**
	 * Performs game logic for doing turns
	 */
	public synchronized void setTurn() {
		
		if(blackPlayer == false)
		{
			setBlackPlayer(true);
			setWhitePlayer(false);
			setLabels();
			if(!blackAgent.getName().equals(AgentName.HUMAN))
			{
				play();
				refreshButtons();
				System.out.println(getCurrentGrid().toString());
				Main.logger.logGrid('B', getCurrentGrid());
			}
		}
		else //if blackPlayer is true
		{
			setBlackPlayer(false);
			setWhitePlayer(true);
			setLabels();
			if(!whiteAgent.getName().equals(AgentName.HUMAN))
			{
				play();
				refreshButtons();
				System.out.println(getCurrentGrid().toString());
				Main.logger.logGrid('W', getCurrentGrid());
			}
		}
	}
	
	///
	// Getters & Setters
	///
	/**
	 * @return the currentGrid
	 */
	public Grid getCurrentGrid() {
		return currentGrid;
	}

	/**
	 * @param currentGrid the currentGrid to set
	 */
	public void setCurrentGrid(Grid currentGrid) {
		this.currentGrid = currentGrid;
	}

	/**
	 * @return the jButtons
	 */
	public JButton[][] getjButtons() {
		return jButtons;
	}

	/**
	 * @param jButtons the jButtons to set
	 */
	public void setjButtons(JButton[][] jButtons) {
		this.jButtons = jButtons;
	}

	/**
	 * @return the blackPlayer
	 */
	public boolean isBlackPlayer() {
		return blackPlayer;
	}

	/**
	 * @param blackPlayer the blackPlayer to set
	 */
	public void setBlackPlayer(boolean blackPlayer) {
		this.blackPlayer = blackPlayer;
	}

	/**
	 * @return the whitePlayer
	 */
	public boolean isWhitePlayer() {
		return whitePlayer;
	}

	/**
	 * @param whitePlayer the whitePlayer to set
	 */
	public void setWhitePlayer(boolean whitePlayer) {
		this.whitePlayer = whitePlayer;
	}

	/**
	 * @return the whiteScore
	 */
	public int getWhiteScore() {
		return whiteScore;
	}

	/**
	 * @param whiteScore the whiteScore to set
	 */
	public void setWhiteScore(int whiteScore) {
		this.whiteScore = whiteScore;
	}

	/**
	 * @return the blackScore
	 */
	public int getBlackScore() {
		return blackScore;
	}

	/**
	 * @param blackScore the blackScore to set
	 */
	public void setBlackScore(int blackScore) {
		this.blackScore = blackScore;
	}

	/**
	 * @return the white
	 */
	public ImageIcon getWhite() {
		return white;
	}

	/**
	 * @param white the white to set
	 */
	public void setWhite(ImageIcon white) {
		MainGamePanel.white = white;
	}

	/**
	 * @return the black
	 */
	public ImageIcon getBlack() {
		return black;
	}

	/**
	 * @param black the black to set
	 */
	public void setBlack(ImageIcon black) {
		MainGamePanel.black = black;
	}

	/**
	 * @return the empty
	 */
	public ImageIcon getEmpty() {
		return empty;
	}

	/**
	 * @param empty the empty to set
	 */
	public void setEmpty(ImageIcon empty) {
		MainGamePanel.empty = empty;
	}

	/**
	 * @return the gridSize
	 */
	public int getGridSize() {
		return GRID_SIZE;
	}

	/**
	 * @return the blackAgent
	 */
	public Agent getBlackAgent() {
		return blackAgent;
	}

	/**
	 * @param blackAgent the blackAgent to set
	 */
	public void setBlackAgent(Agent blackAgent) {
		this.blackAgent = blackAgent;
	}

	/**
	 * @return the whiteAgent
	 */
	public Agent getWhiteAgent() {
		return whiteAgent;
	}

	/**
	 * @param whiteAgent the whiteAgent to set
	 */
	public void setWhiteAgent(Agent whiteAgent) {
		this.whiteAgent = whiteAgent;
	}
}


